﻿using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Windows.Forms;
using WF = System.Windows.Forms;

namespace UpwardsTimeClock.Prototype
{
    public class ControlManager: IDisposable
    {
        #region Public Events

        public event EventHandler Finished;

        private void OnFinished()
        {
            Debug.WriteLine("Finished");
            EventHandler finished = Finished;
            if (finished != null)
            {
                Finished(this, EventArgs.Empty);
            }
        }

        #endregion

        #region Private Fields

        private readonly UpwardsTimeClockConfig _config;
        private readonly Control.ControlCollection _controlCollection;
        private readonly WF.Timer _secondTimer = new WF.Timer();
        private readonly WF.Timer _subSecondTimer = new WF.Timer();

        private readonly List<StartableNode> _waitingControls = new List<StartableNode>();
        private readonly List<StartableNode> _startedControls = new List<StartableNode>();
        private readonly List<SpanningNode> _runningControls = new List<SpanningNode>();

        private readonly List<Spanning> _secondTimerControls = new List<Spanning>();
        private readonly List<Spanning> _subSecondTimerControls = new List<Spanning>();

        private DateTime _startTime;
        private DateTime _stopTime;
        private DateTime? _pauseTime;

        private uint _iteration;

        #endregion

        #region Inner Class

        private class StartableNode
        {
            public Startable Startable { get; private set; }
            public TimeSpan StartTimeSpan { get; set; }

            public StartableNode(Startable startable)
            {
                Startable = startable;
                StartTimeSpan = Program.GetScaledTimeSpan(startable.StartTime);
                Debug.WriteLine(string.Format("{0} starts at {1}", startable.Key, StartTimeSpan));
            }
        }

        private class SpanningNode
        {
            public Spanning Spanning { get; private set; }
            public TimeSpan EndTimeSpan { get; set; }

            public SpanningNode(Spanning spanning)
            {
                Spanning = spanning;
                EndTimeSpan = Program.GetScaledTimeSpan(spanning.EndTime);
                Debug.WriteLine(string.Format("{0} ends at {1}", spanning.Key, EndTimeSpan));
            }
        }

        #endregion

        #region Constructors

        public ControlManager(UpwardsTimeClockConfig config, 
            Control.ControlCollection controlCollection)
        {
            _config = config;
            _controlCollection = controlCollection;

            _secondTimer.Enabled = false;
            _secondTimer.Interval = (int)(1000.0 * Program.Config.TimeScale);
            _secondTimer.Tick += SecondTimerTickHandler;

            _subSecondTimer.Enabled = false;
            _subSecondTimer.Interval = 50;
            _subSecondTimer.Tick += SubSecondTimerTickHandler;
        }

        #endregion

        #region Event Handlers

        private void SubSecondTimerTickHandler(object sender, EventArgs e)
        {
            foreach (Spanning control in _subSecondTimerControls)
            {
                control.TimerTick();
            }
        }

        private void SecondTimerTickHandler(object sender, EventArgs e)
        {
            CheckControls();
            foreach (Spanning control in _secondTimerControls)
            {
                control.TimerTick();
            }
        }

        #endregion

        #region Public Methods

        public void Start(uint iteration)
        {
            Start(iteration, TimeSpan.Zero);
        }

        public void Start(uint iteration, TimeSpan startTimeSpan)
        {
            _iteration = iteration;

            _subSecondTimer.Stop();
            _secondTimer.Stop();

            ClearControls(false);

            _startTime = DateTime.Now - Program.GetScaledTimeSpan(startTimeSpan);
            _stopTime = _startTime + Program.GetScaledTimeSpan(TimeSpan.Parse(_config.Length));
            Debug.WriteLine(string.Format("Start time: {0}", _startTime));
            Debug.WriteLine(string.Format("Stop time: {0}", _stopTime));

            foreach (Startable startable in _config.Actions)
            {
                if (
                    !(startable is Sound) ||
                    TimeSpan.Parse(startable.StartTime) >= startTimeSpan
                    )
                {
                    _waitingControls.Add(new StartableNode(startable));
                }
            }
            _waitingControls.Sort((x, y) => x.StartTimeSpan.CompareTo(y.StartTimeSpan));

            CheckControls();
            _secondTimer.Start();
        }

        private void ClearControls(bool dispose)
        {
            if (_waitingControls != null)
            {
                _waitingControls.ForEach(sn => sn.Startable.Reset() );
                if (dispose) _waitingControls.ForEach(sn => sn.Startable.Dispose());
                _waitingControls.Clear();
            }

            if (_startedControls != null)
            {
                _startedControls.ForEach(sn => sn.Startable.Reset());
                if (dispose) _startedControls.ForEach(sn => sn.Startable.Dispose());
                _startedControls.Clear();
            }

            if (_runningControls != null)
            {
                _runningControls.ForEach(sn => sn.Spanning.Reset());
                if (dispose) _runningControls.ForEach(sn => sn.Spanning.Dispose());
                _runningControls.Clear();
            }

            if (_secondTimerControls != null)
            {
                _secondTimerControls.ForEach(sn => sn.Reset());
                if (dispose) _secondTimerControls.ForEach(sn => sn.Dispose());
                _secondTimerControls.Clear();
            }

            if (_subSecondTimerControls != null)
            {
                _subSecondTimerControls.ForEach(sn => sn.Reset());
                if (dispose) _subSecondTimerControls.ForEach(sn => sn.Dispose());
                _subSecondTimerControls.Clear();
            }
        }

        #endregion

        #region Private Methods

        private void CheckControls()
        {
            DateTime now = DateTime.Now;
            bool addedSpanning = false;

            // Start any controls that need to be started
            CheckStartControls(ref now, ref addedSpanning);

            if (addedSpanning)
            {
                _runningControls.Sort((x, y) => x.EndTimeSpan.CompareTo(y.EndTimeSpan));
            }

            // Stop any controls that need to be stopped
            now = CheckStopControls(now);

            if (now >= _stopTime)
            {
                Stop();
            }
        }

        private DateTime CheckStopControls(DateTime now)
        {
            while (_runningControls.Count > 0 && now >= (_startTime +  _runningControls[0].EndTimeSpan))
            {
                SpanningNode spanningNode = _runningControls[0];
                Spanning spanning = spanningNode.Spanning;
                _runningControls.RemoveAt(0);
                Debug.WriteLine(string.Format("Stopping {0}", spanning.Key));
                spanning.Stop();
                switch (spanning.TimerEventMode)
                {
                    case TimerEventMode.Second:
                        _secondTimerControls.Remove(spanning);
                        break;
                    case TimerEventMode.Subsecond:
                        _subSecondTimerControls.Remove(spanning);
                        if (_subSecondTimerControls.Count == 0)
                        {
                            _subSecondTimer.Enabled = false;
                        }
                        break;
                }
            }
            return now;
        }

        private void CheckStartControls(ref DateTime now, ref bool addedSpanning)
        {
            while (_waitingControls.Count > 0 && now >= (_startTime + _waitingControls[0].StartTimeSpan))
            {
                StartableNode startableNode = _waitingControls[0];
                Startable startable = startableNode.Startable;
                _waitingControls.RemoveAt(0);
                if (!startable.IsInitialized)
                {
                    Debug.WriteLine(string.Format("Starting {0}", startable.Key));
                    startable.Initialize(_controlCollection);
                }
                startable.Start();

                Spanning spanning = startable as Spanning;
                if (spanning != null)
                {
                    addedSpanning = true;
                    _runningControls.Add(new SpanningNode(spanning));
                    switch (spanning.TimerEventMode)
                    {
                        case TimerEventMode.Second:
                            Debug.WriteLine(string.Format("Second timer for {0}", spanning.Key));
                            _secondTimerControls.Add(spanning);
                            break;
                        case TimerEventMode.Subsecond:
                            Debug.WriteLine(string.Format("SubSecond timer for {0}", spanning.Key));
                            _subSecondTimerControls.Add(spanning);
                            if (_subSecondTimerControls.Count == 1)
                            {
                                _subSecondTimer.Enabled = true;
                            }
                            break;
                    }
                }
                else
                {
                    _startedControls.Add(new StartableNode(startable));
                }
            }
        }

        public void Stop()
        {
            _secondTimer.Enabled = _subSecondTimer.Enabled = false;
            foreach (SpanningNode spanningNode in _runningControls)
            {
                spanningNode.Spanning.Stop();
            }
            OnFinished();
        }

        public void Pause()
        {
            if (_pauseTime == null)
            {
                _pauseTime = DateTime.Now;
                _subSecondTimer.Stop();
                _secondTimer.Stop();
            }
        }

        public void Continue()
        {
            if (_pauseTime != null)
            {
                TimeSpan adjustment = (DateTime.Now - _pauseTime.Value);
                _startTime += adjustment;
                _stopTime += adjustment;

                foreach (StartableNode startable in _waitingControls)
                {
                    //startable.StartTimeSpan += adjustment;
                    ISpanningControl spanningControl = 
                        startable.Startable.Control as ISpanningControl;
                    if (spanningControl != null)
                    {
                        spanningControl.AdjustStartTime(adjustment);
                    }
                }

                foreach (SpanningNode spanning in _runningControls)
                {
                    //spanning.EndTimeSpan += adjustment;
                    ((ISpanningControl)(spanning.Spanning.Control)).AdjustStartTime(adjustment);
                }

                _secondTimer.Start();
                if (_subSecondTimerControls.Count > 0) _subSecondTimer.Start();
                _pauseTime = null;
            }
        }

        #endregion


        #region IDisposable Members

        public void Dispose()
        {
            ClearControls(true);
        }

        #endregion
    }
}
